﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using System.Threading;
using System.Data.Entity.Migrations;

namespace EntityFrameworkStudy.Studys
{
    class ConcurrentStudy
    {
        public static void 嵌套事务导致的死锁()
        {
            Console.WriteLine("\n***************嵌套事务导致的死锁***************。");

            var autoResetEvent = new AutoResetEvent(false);
            var writeTransactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromSeconds(120) };
            var readTransactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted, Timeout = TimeSpan.FromSeconds(5) };

            using (var ts1 = new TransactionScope(TransactionScopeOption.Required, writeTransactionOptions))
            {
                using (var context = new StudyContext())
                {
                    Console.WriteLine("\nA事务添加数据，未提交事务。");
                    context.Users.AddOrUpdate(x => x.Title, new User() { Title = "脏读测试数据" });
                    context.SaveChanges();
                }


                try
                {
                    using (var ts2 = new TransactionScope(TransactionScopeOption.Suppress, TimeSpan.FromSeconds(5)))
                    {
                        using (var context = new StudyContext())
                        {
                            Console.WriteLine("\nA事务所在线程使用 TransactionScopeOption.Suppress 读取数据中...");
                            var user = context.Users.FirstOrDefault(x => x.Title == "脏读测试数据");
                            Console.WriteLine("A事务所在线程使用 TransactionScopeOption.Suppress 读取数据:" + user);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.InnerException.Message);
                }

                try
                {
                    using (var ts2 = new TransactionScope(TransactionScopeOption.RequiresNew, readTransactionOptions))
                    {
                        using (var context = new StudyContext())
                        {
                            Console.WriteLine("\nA事务所在线程使用 TransactionScopeOption.RequiresNew 和 IsolationLevel.ReadUncommitted 读取数据中...");
                            var user = context.Users.FirstOrDefault(x => x.Title == "脏读测试数据");
                            Console.WriteLine("A事务所在线程使用 TransactionScopeOption.RequiresNew 和 IsolationLevel.ReadUncommitted 读取数据:" + user);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.InnerException.Message);
                }

                {
                    using (var context = new StudyContext())
                    {
                        var user = context.Users.FirstOrDefault(x => x.Title == "脏读测试数据");
                        Console.WriteLine("\nA事务读取数据:" + user);
                    }
                }
            }
        }

        public static void 脏读测试()
        {
            Console.WriteLine("\n***************重现脏读***************。");
            脏读测试(IsolationLevel.ReadUncommitted);

            Console.WriteLine("\n***************避免脏读***************。");
            脏读测试(IsolationLevel.ReadCommitted);
        }

        private static void 脏读测试(IsolationLevel readIsolationLevel)
        {
            var autoResetEvent = new AutoResetEvent(false);
            var writeTransactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromSeconds(120) };
            var readTransactionOptions = new TransactionOptions { IsolationLevel = readIsolationLevel, Timeout = TimeSpan.FromSeconds(5) };

            using (var ts1 = new TransactionScope(TransactionScopeOption.Required, writeTransactionOptions))
            {
                #region 添加一条脏读测试数据

                using (var context = new StudyContext())
                {
                    Console.WriteLine("\nA事务添加数据，未提交事务。");
                    context.Users.AddOrUpdate(x => x.Title, new User() { Title = "脏读测试数据" });
                    context.SaveChanges();
                }

                #endregion

                #region 在另外一个线程读取

                ThreadPool.QueueUserWorkItem(data =>
                {
                    try
                    {
                        using (var ts3 = new TransactionScope(TransactionScopeOption.RequiresNew, readTransactionOptions))
                        {
                            using (var context = new StudyContext())
                            {
                                Console.WriteLine("\nB事务读取数据中...");
                                var user = context.Users.FirstOrDefault(x => x.Title == "脏读测试数据");
                                Console.WriteLine("B事务读取数据:" + user);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    finally
                    {
                        autoResetEvent.Set();
                    }
                });

                autoResetEvent.WaitOne();
                autoResetEvent.Dispose();

                #endregion
            }
        }

        public static void 不可重复读测试()
        {
            Console.WriteLine("\n***************重现不可重复读***************。");
            不可重复读测试(IsolationLevel.ReadCommitted);

            Console.WriteLine("\n***************避免不可重复读***************。");
            不可重复读测试(IsolationLevel.RepeatableRead);
        }

        private static void 不可重复读测试(IsolationLevel readIsolationLevel)
        {
            //测试数据准备-开始
            using (var context = new StudyContext())
            {
                context.Users.AddOrUpdate(x => x.Title, new User() { Title = "不可重复读测试数据" });
                context.SaveChanges();
            }
            //测试数据准备-完成

            var autoResetEvent = new AutoResetEvent(false);
            var readTransactionOptions = new TransactionOptions { IsolationLevel = readIsolationLevel, Timeout = TimeSpan.FromSeconds(120) };
            var writeTransactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromSeconds(5) };

            using (var ts1 = new TransactionScope(TransactionScopeOption.Required, readTransactionOptions))
            {
                using (var context = new StudyContext())
                {
                    var user = context.Users.FirstOrDefault(x => x.Title.Contains("不可重复读测试数据"));
                    Console.WriteLine("\nA事务第一次读取：" + user.Title);
                }

                ThreadPool.QueueUserWorkItem(data =>
                {
                    try
                    {
                        using (var ts2 = new TransactionScope(TransactionScopeOption.Required, writeTransactionOptions))
                        {
                            using (var context = new StudyContext())
                            {
                                Console.WriteLine("\nB事务中间修改，并提交事务。");
                                var user = context.Users.FirstOrDefault(x => x.Title.Contains("不可重复读测试数据"));
                                user.Title = user.Title + "-段光伟";
                                context.SaveChanges();
                            }
                            ts2.Complete();
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    finally
                    {
                        autoResetEvent.Set();
                    }
                });

                autoResetEvent.WaitOne();
                autoResetEvent.Dispose();

                using (var context = new StudyContext())
                {
                    var user = context.Users.FirstOrDefault(x => x.Title.Contains("不可重复读测试数据"));
                    Console.WriteLine("\nA事务第二次读取：" + user.Title);
                }
            }

            //测试数据清理-开始
            using (var context = new StudyContext())
            {
                var user = context.Users.FirstOrDefault(x => x.Title.Contains("不可重复读测试数据"));
                context.Users.Remove(user);
                context.SaveChanges();
            }
            //测试数据清理-完成
        }

        public static void 幻读测试()
        {
            Console.WriteLine("\n***************重现幻读***************。");
            幻读测试(IsolationLevel.RepeatableRead);

            Console.WriteLine("\n***************避免幻读***************。");
            幻读测试(IsolationLevel.Serializable);
        }

        private static void 幻读测试(IsolationLevel readIsolationLevel)
        {
            var autoResetEvent = new AutoResetEvent(false);
            var readTransactionOptions = new TransactionOptions { IsolationLevel = readIsolationLevel, Timeout = TimeSpan.FromSeconds(120) };
            var writeTransactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromSeconds(5) };

            using (var ts1 = new TransactionScope(TransactionScopeOption.Required, readTransactionOptions))
            {
                using (var context = new StudyContext())
                {
                    var user = context.Users.FirstOrDefault(x => x.Title.Contains("幻读测试数据"));
                    Console.WriteLine("\nA事务第一次读取：" + user);
                }

                ThreadPool.QueueUserWorkItem(data =>
                {
                    try
                    {
                        using (var ts2 = new TransactionScope(TransactionScopeOption.Required, writeTransactionOptions))
                        {
                            using (var context = new StudyContext())
                            {
                                Console.WriteLine("\nB事务中间添加，并提交事务。");
                                context.Users.Add(new User() { Title = "幻读测试数据" });
                                context.SaveChanges();
                            }
                            ts2.Complete();
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    finally
                    {
                        autoResetEvent.Set();
                    }
                });

                autoResetEvent.WaitOne();
                autoResetEvent.Dispose();

                using (var context = new StudyContext())
                {
                    var user = context.Users.FirstOrDefault(x => x.Title.Contains("幻读测试数据"));
                    Console.WriteLine("\nA事务第二次读取：" + user);
                }
            }

            //测试数据清理-开始
            using (var context = new StudyContext())
            {
                var user = context.Users.FirstOrDefault(x => x.Title.Contains("幻读测试数据"));
                if (user != null)
                {
                    context.Users.Remove(user);
                    context.SaveChanges();
                }
            }
            //测试数据清理-完成
        }
    }
}
